//
//  main.c
//  shell
//
//  Created by 徐志瀚 on 17/10/18.
//  Copyright © 2017年 徐志瀚. All rights reserved.
//

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
#include <errno.h>

#define MAXLEN 80

char cwd[MAXLEN];//当前工作目录
char *prompt;

char* parseCmd(char* cmd, char** argarr, int* argc)
{
    enum states {S_START, S_INTOKEN, S_INQUOTES};
    int numArgs = 0;//参数数目
    int loop = 1;//循环标志
    enum states state = S_START;//当前状态
    int lastch;
    
    while(loop)
    {
        switch(state)
        {
            case S_START:
                if(*cmd == '"')
                {
                    *argarr++ = cmd + 1;
                    numArgs++;
                    state = S_INQUOTES;
                }
                else if(*cmd == 0 || *cmd == ';')
                    loop = 0;
                else if(*cmd <= ' ')//忽略ascii码表中的空格符及空格符前面的字符
                    *cmd = 0;
                else
                {
                    *argarr++ = cmd;
                    numArgs++;
                    state = S_INTOKEN;
                }
                break;
            case S_INTOKEN:
                if(*cmd == 0 || *cmd == ';')
                    loop = 0;
                else if(*cmd <= ' ')
                {
                    *cmd = 0;
                    state = S_START;
                }
                break;
            case S_INQUOTES:
                if(*cmd == 0)
                    loop = 0;
                else if(*cmd == '"')
                {
                    *cmd = 0;
                    state = S_START;
                }
                break;
        }
        cmd++;//指向下一个字符
    }
    *argarr = NULL;//最后一个参数指向空
    
    if(argc != NULL) *argc = numArgs;
    
    lastch = cmd[-1];
    cmd[-1] = 0;
    return lastch == ';' ? cmd : NULL;// 如果遇到分号则返回剩余的字符串作为下一条命令
}

int main(int argc, char** argv)
{
    char cmd[80];
    char* source = NULL;
    char* arg[20];
    int statval;
    int numArgs;
    
    while(1)
    {
        if(source == NULL)
        {
            getcwd(cwd,MAXLEN);
            printf("%s$ ",cwd);
            //读取命令
            if((source = gets(cmd)) == NULL)
                exit(0);
        }
        
        source = parseCmd(source, arg, &numArgs);
        if(numArgs == 0) continue;
        
        //是否退出程序
        if(strcasecmp(arg[0], "exit") == 0)
        {
            exit(0);//不管exit后面是否还有参数，直接退出
        }
        
        if(!strcmp(arg[0],"cd"))//切换父进程的工作目录
        {
            chdir(arg[1]);
            continue;
        }
        
        if(!strcmp(arg[numArgs-1],"&"))//后台命令
        {
            char* a[20];//临时变量
            int k;
            for(k=0;k<numArgs-1;k++)
            {
                a[k] = malloc(strlen(arg[k]) + 1);
                strcpy(a[k],arg[k]);
                //printf("%s ", a[k]);
            }
            a[k] = NULL;
            if(fork() == 0)
            {
                if(execvp(a[0],a) == -1)// 执行失败
                    fprintf(stderr, "exec %s failed: %s\n",argv[0],strerror(errno));
                exit(1);//退出子进程
            }
            for(k=0;k<numArgs-1;k++);//释放内存
            free(a[k]);
            continue;
        }
        
        //创建子进程执行命令
        if(fork() == 0)
        {
            if(execvp(arg[0],arg) == -1)// 执行失败
                fprintf(stderr, "exec %s failed: %s\n",argv[0],strerror(errno));  
            exit(1);//退出子进程  
        }  
        wait(&statval);  
    }  
}